package handler

import (
	"encoding/json"
	"sync/atomic"
	"time"

	"starsine.cn/appTpl/cmn"
	"starsine.cn/appTpl/cmn/config/aconf"
	. "starsine.cn/appTpl/cmn/defs"
	"starsine.cn/appTpl/cmn/logs"
	. "starsine.cn/appTpl/svccenter/registry/restful/openapi"
	"starsine.cn/appTpl/svcregistry/restful/ws-client"
)

type STATE int

const (
	REGISTRY STATE = iota
	HEARTBEAT_CHECK
)

func (t STATE) String() string {
	switch t {
	case REGISTRY:
		return "registry"
	case HEARTBEAT_CHECK:
		return "heartbeat-check"
	}
	return "unknown-state"
}

type SvcHandler struct {
	snRegistry  atomic.Int32
	snHeartbeat atomic.Int32
	hbFailCnt   uint32

	smSwitch   chan STATE
	state      STATE
	instanceID string
	appCtxt    *AppContext
}

func CreateSvcHandler(appCtxt *AppContext) *SvcHandler {
	p := new(SvcHandler)
	p.snRegistry.Store(0)
	p.snHeartbeat.Store(0)
	p.hbFailCnt = 0

	p.smSwitch = make(chan STATE)
	p.state = REGISTRY

	p.instanceID = ""
	p.appCtxt = appCtxt

	return p
}

func (p *SvcHandler) Start() {
	cmn.GetEvntEmiter().On("ws-broken", func(params ...interface{}) {
		p.smSwitch <- REGISTRY
	})
	ws.RegisterProc(p.wsMsgHandler)

	go p.sm()
	p.smSwitch <- REGISTRY
}

func (p *SvcHandler) sm() {
	logs.Infof("restful.SvcHandler sm routine start")
	p.appCtxt.Wg.Add(1)
	defer func() {
		logs.Warnf("restful.SvcHandler sm routine exit")
		p.appCtxt.Wg.Done()
	}()

	ticker := time.NewTicker(time.Second)
	defer ticker.Stop()

	for {
		select {
		case s := <-p.smSwitch:
			logs.Debugf("switch to %s", s)
			switch s {
			case REGISTRY:
				ticker.Reset(1 * time.Second)
			case HEARTBEAT_CHECK:
				ticker.Reset(15 * time.Second)
			}
			p.state = s

		case <-ticker.C:
			switch p.state {
			case REGISTRY:
				p.registry()
			case HEARTBEAT_CHECK:
				p.heartbeatCheck()
			}

		case <-p.appCtxt.Ctx.Done():
			logs.Warnf("restful.SvcHandler sm routine ctxt done to quit")
			return
		}
	}
}

func (p *SvcHandler) registry() {
	p.snRegistry.Add(1)
	err := ws.SendMessage(&RegistryRequest{
		MsgHead: WsHead{
			InstanceID:  p.instanceID,
			ResOrReq:    "req",
			RequestType: "svc-registry",
			SerialNum:   p.snRegistry.Load(),
		},
		Data: SvcInfo{
			UserSpace: aconf.GetString("UserSpace", "default", aconf.WithENV("svc_userspace")),
			SvcType:   aconf.GetString("SvcType", "default", aconf.WithENV("svc_type")),
			Name:      aconf.GetString("NodeName", "default", aconf.WithENV("node_name")),
			Rpc:       aconf.GetString("RPC.Addr", "localhost:20001", aconf.WithENV("rpc_addr")),
		},
	})
	if err != nil {
		logs.Warnf("restful.SvcHandler registry request fail, err: %s", err)
		return
	} else {
		logs.Debugf("restful.SvcHandler registry request ok")
	}
}

func (p *SvcHandler) heartbeatCheck() {
	p.hbFailCnt++
	if p.hbFailCnt > 3 {
		go func() {
			p.smSwitch <- REGISTRY
		}()
		return
	}

	p.snHeartbeat.Add(1)
	err := ws.SendMessage(&HeartbeatRequest{
		MsgHead: WsHead{
			InstanceID:  p.instanceID,
			ResOrReq:    "req",
			RequestType: "svc-heartbeat",
			SerialNum:   p.snHeartbeat.Load(),
		},
	})
	if err != nil {
		logs.Warnf("restful.SvcHandler heartbeatCheck fail, err: %s", err)
	} else {
		logs.Debugf("restful.SvcHandler heartbeatCheck req send ok")
	}
}

func (p *SvcHandler) wsMsgHandler(msg []byte) {
	head := WsRequestTpl{}
	err := json.Unmarshal(msg, &head)
	if err != nil {
		logs.Warnf("wsMsgHandler Unmarshal fail, err: %s", err)
		return
	}

	switch head.MsgHead.RequestType {
	case "ws-heartbeat":
		logs.Debugf("wsMsgHandler get ws-heartbeat")
	case "svc-heartbeat":
		if head.MsgHead.ResOrReq == "req" {
			p.heartbeatRequestHandle(msg, head.MsgHead)
		} else {
			p.heartbeatResponseHandle(msg, head.MsgHead)
		}

	case "svc-registry":
		if p.snRegistry.Load() == head.MsgHead.SerialNum {
			p.registryResonseHandle(msg, head.MsgHead)
			logs.Debugf("wsMsgHandler get svc-registry response")
		} else {
			logs.Debugf("wsMsgHandler get obsolete svc-registry response")
		}

	default:
		logs.Warnf("wsMsgHandler unknown request type: %s", head.MsgHead.RequestType)
	}
}

func (p *SvcHandler) heartbeatRequestHandle(msg []byte, head WsHead) {
	data := &HeartbeatResponse{
		MsgHead: WsHead{
			InstanceID:  p.instanceID,
			SerialNum:   head.SerialNum,
			ResOrReq:    "res",
			RequestType: "svc-heartbeat",
			Message:     "heartbeat ok",
			Status:      "ok",
		},
	}
	if err := ws.SendMessage(data); err != nil {
		logs.Warnf("restful.heartbeatResponse SendMessage fail, err: %s", err)
		return
	} else {
		logs.Debugf("restful.SvcHandler heartbeatRequestHandle get svc-heartbeat request and response ok")
	}

}

func (p *SvcHandler) heartbeatResponseHandle(msg []byte, head WsHead) {
	p.hbFailCnt = 0
	logs.Debugf("restful.SvcHandler heartbeatResponseHandle svc-heartbeat response ok")
}

func (p *SvcHandler) registryResonseHandle(msg []byte, head WsHead) {
	data := RegistryResponse{}

	err := json.Unmarshal(msg, &data)
	if err != nil {
		logs.Warnf("restful.registryResonseHandle Unmarshal fail, err: %s", err)
		return
	}

	if data.MsgHead.Status != "ok" {
		logs.Warnf("restful.registryResonseHandle fail, err: %s", data.MsgHead.Message)
		return
	}

	p.instanceID = data.Data.InstanceID
	p.hbFailCnt = 0
	p.smSwitch <- HEARTBEAT_CHECK
	logs.Debugf("restful.registryResonseHandle ok, instance id = %s", data.Data.InstanceID)
}
